package com.fsnbigdata.web;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fsnbigdata.domain.Organism;
import com.fsnbigdata.domain.Taxonomy;
import com.fsnbigdata.service.api.DistributionService;
import com.fsnbigdata.service.api.HintService;
import com.fsnbigdata.service.api.OrganismService;
import com.fsnbigdata.util.Naming;
import com.fsnbigdata.util.ObjectNull;
import com.fsnbigdata.util.TaxonomyHintEnum;
import com.fsnbigdata.util.TaxonomyJsonUtils;
import com.fsnbigdata.vo.HintVo;
import com.fsnbigdata.vo.Tree;
import io.swagger.annotations.*;
import net.sf.json.JSONObject;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.roo.addon.web.mvc.controller.annotations.ControllerType;
import org.springframework.roo.addon.web.mvc.controller.annotations.RooController;
import org.springframework.roo.addon.web.mvc.controller.annotations.responses.json.RooJSON;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Created by zhongcheng on 19/09/2017.
 */

@RooController(entity =Taxonomy.class, pathPrefix = "/api", type = ControllerType.COLLECTION)
@RooJSON
@RestController
@RequestMapping(value = "/api/hint", name = "HintController", produces = MediaType.APPLICATION_JSON_VALUE)
@Api(tags = "Hint", description = "数据提示" )
public class HintController {

//    public static UriComponents listURI() {
//        return MvcUriComponentsBuilder.fromMethodCall(MvcUriComponentsBuilder.on(HintController.class).list(null, null)).build().encode();
//    }
//
//    public static UriComponents showURI(LocationHint locationHint) {
//        return MvcUriComponentsBuilder.fromMethodCall(MvcUriComponentsBuilder.on(HintController.class).show(locationHint)).buildAndExpand(locationHint.getId()).encode();
//    }

    private HintService hintService;

    private OrganismService organismService;

    public static String taxonomy;

    @Autowired
    private DistributionService distributionService;

    @Autowired
    public HintController(HintService hintService, OrganismService organismService) {
        this.hintService = hintService;
        this.organismService = organismService;
    }

    @GetMapping(name = "get", value = "/habitat")
//    @PreAuthorize("hasRole('ROLE_USER')")
    public ResponseEntity<?> habitat() throws IOException {
        try(InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("habitat.json")){
            String content = IOUtils.toString(inputStream, "UTF-8");
            ObjectMapper om = new ObjectMapper();
            return ResponseEntity.ok(om.readTree(content));
        }
    }

    @GetMapping(name = "get", value = "/location")
    @PreAuthorize("hasRole('USER')")
    public ResponseEntity<?> location() throws IOException {
        try(InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("location.json")){
            String content = IOUtils.toString(inputStream, "UTF-8");
            ObjectMapper om = new ObjectMapper();
            return ResponseEntity.ok(om.readTree(content));
        }
    }

    @GetMapping(name = "get", value = "/natureReserve")
    @PreAuthorize("hasRole('USER')")
    public ResponseEntity<?> natureReserve() throws IOException {
        try(InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("nature_reserve.json")){
            String content = IOUtils.toString(inputStream, "UTF-8");
            ObjectMapper om = new ObjectMapper();
            return ResponseEntity.ok(om.readTree(content));
        }
    }

    @GetMapping(name = "get", value = "/taxonomy")
    @PreAuthorize("hasRole('USER')")
    public ResponseEntity<?> taxonomy() throws IOException {
        try(InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("taxonomy.json")){
            String content = IOUtils.toString(inputStream, "UTF-8");
            ObjectMapper om = new ObjectMapper();
            return ResponseEntity.ok(om.readTree(content));
        }
    }


    @GetMapping(name = "get", value = "/taxonomyQuery")
    @PreAuthorize("hasRole('USER')")
    public ResponseEntity<?> taxonomyQuery(
            TaxonomyHintEnum target,
            @RequestParam(defaultValue = "", required = false) String parent,
            @RequestParam(required = false) String query) throws IOException {

        if(StringUtils.isEmpty(taxonomy)){
            try(InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("taxonomy.json")){
                taxonomy = IOUtils.toString(inputStream, "UTF-8");
            }
        }

        List<Naming> standard = Collections.EMPTY_LIST;
        List<Naming> database = Collections.EMPTY_LIST;

        if(StringUtils.isEmpty(query)) {
            if(target == TaxonomyHintEnum.Kingdom){
                standard = TaxonomyJsonUtils.readKingdom(taxonomy);
            }else if(target == TaxonomyHintEnum.Phylumn){
                standard = TaxonomyJsonUtils.readPhylumn(taxonomy, parent);
            }else if(target == TaxonomyHintEnum.tClass){
                standard = TaxonomyJsonUtils.readTClass(taxonomy, parent);
            }
        }else{
            if(target == TaxonomyHintEnum.Kingdom){
                standard = TaxonomyJsonUtils.readKingdom(taxonomy, query);
            }else if(target == TaxonomyHintEnum.Phylumn){
                standard = TaxonomyJsonUtils.readPhylumn(taxonomy, parent, query);
            }else if(target == TaxonomyHintEnum.tClass){
                standard = TaxonomyJsonUtils.readTClass(taxonomy, parent, query);
            }
        }


        if(StringUtils.isEmpty(query)) {
            query = "%%";
        }else{
            query = "%" + query + "%";
        }

        if(target == TaxonomyHintEnum.Kingdom){
            database = hintService.findByKingdomLike(query).stream()
                    .map(it -> new Naming(it.getKingdom(), it.getKingdomLatin()))
                    .collect(Collectors.toList());
        }else if(target == TaxonomyHintEnum.subKingdom){
            database = hintService.findByKingdomAndSubKingdomLike(parent, query).stream()
                    .map(it -> new Naming(it.getSubKingdom(), it.getSubKingdomLatin()) )
                    .collect(Collectors.toList());
        }else if(target == TaxonomyHintEnum.Phylumn){
                database = hintService.findByKingdomAndPhylumnLike(parent, query).stream()
                        .map(it -> new Naming(it.getPhylumn(), it.getPhylumnLatin()) )
                        .collect(Collectors.toList());
        }else if(target == TaxonomyHintEnum.subPhylumn){
            database = hintService.findByPhylumnAndSubPhylumnLike(parent, query).stream()
                    .map(it -> new Naming(it.getSubPhylumn(), it.getSubPhylumnLatin()) )
                    .collect(Collectors.toList());
        }else if(target == TaxonomyHintEnum.tClass){
            database = hintService.findByPhylumnAndTClassLike(parent, query).stream()
                    .map(it -> new Naming(it.getTClass(), it.getTClassLatin()) )
                    .collect(Collectors.toList());
        }else if(target == TaxonomyHintEnum.subClass){
            database = hintService.findByTClassAndSubClassLike(parent, query).stream()
                    .map(it -> new Naming(it.getSubClass(), it.getSubClassLatin()) )
                    .collect(Collectors.toList());
        }else if(target == TaxonomyHintEnum.tOrder){
            database = hintService.findByTClassAndTOrderLike(parent, query).stream()
                    .map(it -> new Naming(it.getTOrder(), it.getTOrderLatin()) )
                    .collect(Collectors.toList());
        }else if(target == TaxonomyHintEnum.subOrder){
            database = hintService.findByTOrderAndSubOrderLike(parent, query).stream()
                    .map(it -> new Naming(it.getSubOrder(), it.getSubOrderLatin()) )
                    .collect(Collectors.toList());
        }else if(target == TaxonomyHintEnum.family){
            database = hintService.findByTOrderAndFamilyLike(parent, query).stream()
                    .map(it -> new Naming(it.getFamily(), it.getFamilyLatin()) )
                    .collect(Collectors.toList());
        }else if(target == TaxonomyHintEnum.subFamily){
            database = hintService.findByFamilyAndSubFamilyLike(parent, query).stream()
                    .map(it -> new Naming(it.getSubFamily(), it.getSubFamilyLatin()) )
                    .collect(Collectors.toList());
        }else if(target == TaxonomyHintEnum.genus){
            database = hintService.findByFamilyAndGenusLike(parent, query).stream()
                    .map(it -> new Naming(it.getGenus(), it.getGenusLatin()) )
                    .collect(Collectors.toList());
        }else if(target == TaxonomyHintEnum.subGenus){
            database = hintService.findByGenusAndSubGenusLike(parent, query).stream()
                    .map(it -> new Naming(it.getSubGenus(), it.getSubGenusLatin()) )
                    .collect(Collectors.toList());
        }else if(target == TaxonomyHintEnum.species){
            database = hintService.findByGenusAndSpeciesLike(parent, query).stream()
                    .map(it -> new Naming(it.getSpecies(), it.getSpeciesLatin()) )
                    .collect(Collectors.toList());
        }else if(target == TaxonomyHintEnum.subSpecies){
            database = hintService.findBySpeciesAndSubSpeciesLike(parent, query).stream()
                    .map(it -> new Naming(it.getSubSpecies(), it.getSubSpeciesLatin()) )
                    .collect(Collectors.toList());
        }


        Set<Naming> ret = new HashSet<>();
        Stream.of(standard, database).forEach(ret::addAll);
        return ResponseEntity.ok(ret);
    }

    @GetMapping(name = "get", value = "/conservationStatus")
    @PreAuthorize("hasRole('USER')")
    public ResponseEntity<?> conservationStatus() throws IOException {
        try(InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("conservation_status.json")){
            String content = IOUtils.toString(inputStream, "UTF-8");
            ObjectMapper om = new ObjectMapper();
            return ResponseEntity.ok(om.readTree(content));
        }
    }

    @GetMapping(name = "get", value = "/organismName")
    @PreAuthorize("hasRole('USER')")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", defaultValue = "0", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "size", defaultValue = "20", dataType = "int", paramType = "query")
    })
    public ResponseEntity<?> organismName(
            @ApiParam @RequestParam String query, Pageable pageable) {
        if(StringUtils.isNotBlank(query)){
            query = "%" + query + "%";
        }
        Page<String> page = this.organismService.findNameByNameLike(query, pageable);
        return ResponseEntity.ok(page);
    }

    /**
     * 获取界
     * @return
     */
    @GetMapping(name = "get", value = "/getTree")
    public List<Tree> getTree(){

        //获取所有界
        List<Object> kingdomList = hintService.findAllByKingdomAndKingdomLatin();
        List<Object> objectList=new ArrayList<>();

        //获取贵州省下物种id
        List<BigInteger> organismList = distributionService.geOrganismIdPronvince();
        for (Object string:kingdomList) {
            int flag = 0;//标识
            Object[] objects = (Object[]) string;
            if (objects[0] != null) {
                //通过获取界查询物种id
                List<BigInteger> bigIntegerList = hintService.getByKingdom(objects[0].toString());
                //将物种id和贵州省物种id作比较
                for (BigInteger integer : bigIntegerList) {
                    for (BigInteger integerList : organismList) {
                        if (integer.equals(integerList)) {
                            flag = 1;
                            objectList.add(objects);
                            break;
                        }
                    }
                    if (flag==1){
                        break;
                    }
                }
            }

        }
        //重新组合树
        return getList(objectList);
    }

    /**
     * 获取树结构
     * @return
     */
    @GetMapping(name = "get", value = "/getByTree")
    public List<Tree> findAllByTree( HttpServletRequest request){
        String kingdom = request.getParameter("kingdom");  //界
        String phylumn = request.getParameter("phylumn");  //门
        String tClass = request.getParameter("tClass");  //纲
        String tOrder = request.getParameter("tOrder");  //目
        String family = request.getParameter("family");  //科
        String genus = request.getParameter("genus");  //属

        //获取树
        List<BigInteger> bigIntegerList = hintService.findAllByOrganism(kingdom,phylumn,tClass,tOrder,family,genus);

        List<Object> voList=new ArrayList<>();
        int flag=0;//标识
        //获取贵州省下物种id
        List<BigInteger> organismList = distributionService.geOrganismIdPronvince();
        for (BigInteger integer:bigIntegerList){
            for (BigInteger integerList:organismList){
                if (integer.equals(integerList)){
                    flag=1;
                    //查询物种id是否属于分类id
                    voList = hintService.findAllTree(kingdom,phylumn,tClass,tOrder,family,genus);
                    break;
                }
            }
            if (flag==1){
                break;
            }
        }
        return getList(voList);

    }

    /**
     * 点击树结构获取物种详情
     * @return
     */
    @GetMapping(name = "get", value = "/findAllByOrganism")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", defaultValue = "0", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "size", defaultValue = "10", dataType = "int", paramType = "query")
    })
    public List findAllByOrganism(HttpServletRequest request, Pageable pageable){
        String kingdom = request.getParameter("kingdom");  //界
        String phylumn = request.getParameter("phylumn");  //门
        String tClass = request.getParameter("tClass");  //纲
        String tOrder = request.getParameter("tOrder");  //目
        String family = request.getParameter("family");  //科
        String genus = request.getParameter("genus");  //属
        //获取树
        List<BigInteger> bigIntegerList = hintService.findAllByOrganism(kingdom,phylumn,tClass,tOrder,family,genus);
        List<Long> bigIntegerList1=new ArrayList<>();

        //获取贵州省下物种id
        List<BigInteger> organismList = distributionService.geOrganismIdPronvince();
        for (BigInteger integer:bigIntegerList){
            for (BigInteger integerList:organismList){
                if (integer.equals(integerList)){
                    bigIntegerList1.add(integer.longValue());
                }
            }
        }
        Pageable pageable1=new PageRequest(pageable.getPageNumber(),pageable.getPageSize());
        //根据物种id获取物种
        return  organismService.getPageList1(bigIntegerList1, pageable1);

    }

    /**
     * 获取界
     * @return
     */
    @GetMapping(name = "get", value = "/findAllByGenusTree")
    public List<Tree> findAllByGenusTree(@RequestParam(required = false) String name){
        //获取树
        List<Object> voList = hintService.findAllByGenusTree(name);
        return getList(voList);

    }

    /**
     * 将字符串对象转换为List对象
     * @param voList
     * @return
     */
    public List<Tree> getList(List<Object> voList){
        List<Tree> hintVoList=new ArrayList<>();
        List<Tree> trees=new ArrayList<>();
        for (Object string:voList){
            Object[] objects= (Object[]) string;
            Tree hintVo = new Tree();
            if (objects[0]!=null){
                //排除为空情况
                if ("".equals(objects[0])){
                    continue;
                }
                if (objects.length>2){
                    hintVo.setName(ObjectNull.takeOutNull(objects[0])+" "+ObjectNull.takeOutNull(objects[1])+" "+ObjectNull.takeOutNull(objects[2])+" "+ObjectNull.takeOutNull(objects[3])+" "+ObjectNull.takeOutNull(objects[4]));
                }else{
                    hintVo.setName((ObjectNull.takeOutNull(objects[1])+" "+ObjectNull.takeOutNull(objects[0])).trim());
                    if (objects[0]!=null&&objects[0].toString().contains("属")){
                        hintVo.setChildren(null);
                    }else{
                        hintVo.setChildren(trees);
                    }
                }
                hintVoList.add(hintVo);
            }

        }
        return  hintVoList;
    }


}
